package model.service;

import it.sauronsoftware.ftp4j.FTPAbortedException;
import it.sauronsoftware.ftp4j.FTPClient;
import it.sauronsoftware.ftp4j.FTPDataTransferException;
import it.sauronsoftware.ftp4j.FTPException;
import it.sauronsoftware.ftp4j.FTPFile;
import it.sauronsoftware.ftp4j.FTPIllegalReplyException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import model.DbAccess.ConnectionManager;
import model.dao.CountryDao;
import model.dto.Country;
import model.dto.Location;
import model.service.threads.CountryThread;
import model.service.threads.DatabaseCallerThread;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.quartz.jobs.FileScanJob;
import org.apache.commons.io.*;
import controller.main.Executer;

public class ChubJobs {

	/**
	 * @param args
	 */

	static Logger logger = Logger.getLogger(ChubJobs.class);
	
	
	public void FTPFiles(String country, List<String> interfaces)
	{
	// loop over the country to get its locations to begin FTP for each
		
		logger.info("Begin FTP" + country + ", " +  interfaces );
		
		CountryDao countryDao = new CountryDao();
		List locations = countryDao.getCountryLocations(country);
		for (int r = 0 ; r < locations.size();r++) 
		{
			List downloadedFilesNames = getFilesFromFTPLocation((Location)locations.get(r),interfaces);
			System.out.println("downloadedFilesNames are: "+downloadedFilesNames.toString());
		}
		logger.info("End FTP" + country + ", " +  interfaces );
	  return ;	
	}
	
	
	
	public void ProcessFilesAtDatabase(String country, List<String>  interfaces)
	{
		logger.info("Begin Processing Files " + country + ", " +  interfaces );
		ConnectionManager connectionManager = new ConnectionManager();
		
		if (interfaces==null) {
			connectionManager.callDBToLoadFiles(country,null,null);
		} else 
		{
				ArrayList<Thread> threads = new ArrayList<Thread>();
				
				for (int r = 0 ; r < interfaces.size();r++) 
				{
					logger.info("thread starting to load country "+ country + " and interface " + interfaces.get(r));
					Thread dbCallingThread =  new Thread(new DatabaseCallerThread(country,interfaces.get(r),null));
					dbCallingThread.setName("Database calling Thread for country " + country + " and interface " + interfaces.get(r));
					dbCallingThread.start();
					threads.add(dbCallingThread);
					logger.info("thread finished to load country "+ country + " and interface " + interfaces.get(r));
				}
				
				int running = 0;
				do {
					running = 0;
					for (Thread thread : threads) {
						if (thread.isAlive()) {
							running++;
						}
					}
				} while (running > 0);
				System.out.println("All Database calling threads has been finished for country "+ country );
			
			
		}
		logger.info("End Processing Files " + country + ", " +  interfaces );
	  return ;	
	}


	public void SendNotificationMails(String country, List<String>  interfaces)
	{
		//SEND_NOTIFICATION_MAILS
		logger.info("Begin Sending notifications " + country + ", " +  interfaces );
		ConnectionManager connectionManager = new ConnectionManager();
		
		if (interfaces != null) 
		{
			for (int r = 0 ; r < interfaces.size();r++) 
			{
				connectionManager.sendNotifications(country,interfaces.get(r));
			}
		}
		logger.info("End  Sending notifications " + country + ", " +  interfaces );
	  return ;	
	}
	
	//Get local paths
	public Map<String,String> getLocalPaths(List<String> countries){
		Map<String,String> localPaths=new HashMap<String, String>();
		for(int cntry=0;cntry<countries.size();cntry++)
		{
			CountryDao cd = new CountryDao();
			List<Location> cLocations = cd.getCountryLocations(countries.get(cntry));
			String localPath=cLocations.get(0).getLocalPath();
			localPaths.put(countries.get(cntry),localPath);
		}
		return localPaths;
	}
	
	//Get paths of files for each country filtered by modified date
	public Map<String,List<String>> getFilesPaths(Map<String,String> localPaths){
		Map<String,List<String>> filesPaths=new HashMap<String, List<String>>();
		for (String key: localPaths.keySet()) {
			List<String> filesPathsPerCountry=new ArrayList<String>();
        	String localPath=localPaths.get(key);
        	File dir = new File(localPath);
	        File [] files  = dir.listFiles();
	        Date now = new Date();
	        GregorianCalendar gc = new GregorianCalendar();
	        gc.setTime(now);
	        CountryDao cd = new CountryDao();
			List<Location> cLocations = cd.getCountryLocations(key);
			int months=cLocations.get(0).getArchivingPeriod();
	        gc.add(Calendar.MONTH, -(months));
	        Date archiveDate = gc.getTime();
	        if(files==null){
	        	logger.info("Directory  "+localPath+" has no files" );
	        }
	        else{
	        	for(int f=0;f<files.length;f++){
		        	if(files[f].lastModified() < archiveDate.getTime()){
		        		filesPathsPerCountry.add(files[f].getAbsolutePath());
		        	}
		        }
		        filesPaths.put(key, filesPathsPerCountry);
	        }
        }
		return filesPaths;
	}
	
	public void cut(String src,String dst)throws Exception
	{
		//Define file objects from source and destination
		File srcFile =new File(src);
	    String fileName=srcFile.getName();
	    File dstFile =new File(dst+fileName);
	    
	  //Copy source file/directory
		if(srcFile.isDirectory())
		{
			FileUtils.copyDirectory(srcFile, dstFile);
		}
		else{
			FileUtils.copyFile(srcFile, dstFile);
		}
	    
		//Delete source file/directory
		if(srcFile.isDirectory()){
			FileUtils.deleteDirectory(srcFile);
		}
		else{
		    if(dstFile.lastModified() != srcFile.lastModified())
		    {
		    	logger.info("Destination file"+dstFile.getAbsolutePath()+" is not modified" );
		    }
		    else
		    {
		    	boolean isDeleted=srcFile.delete();
	    	    if(!isDeleted)
	    	    {
	   	    	  logger.info("Destination file "+dstFile.getAbsolutePath()+" is modified and source file"+srcFile.getAbsolutePath()+" is not deleted");
	    	    }
		    }
		}
		    	    
		
//		    	    inStream = new FileInputStream(afile);
//		    	    outStream = new FileOutputStream(bfile);
//		    	    byte[] buffer = new byte[1024];
//		    	    int length;
//		    	    //copy the file content in bytes 
//		    	    while ((length = inStream.read(buffer)) > 0){
//		    	    	outStream.write(buffer, 0, length);
//		    	    }
//		    	    outStream.flush();
//		    	    
//		    	    FileUtils.copyDirectory(srcDir, destDir) 
//		    	    
//					inStream.close();
//		    	    outStream.close();
//		    	    
//		    	    boolean isModified=bfile.setLastModified(afile.lastModified());
//		    	    if(isModified)
//		    	    {
//		    	    	boolean isDeleted=afile.delete();
//			    	    if(!isDeleted)
//			    	    {
//			   	    	  logger.info("Destination file "+bfile.getAbsolutePath()+" is modified and source file"+afile.getAbsolutePath()+" is not deleted");
//			    	    }
//		    	    	
//		    	    }
//		    	    else
//		    	    {
//		    	    	logger.info("Destination file"+bfile.getAbsolutePath()+" is not modified" );
//		    	    }
//		    	    
//		}
//		catch(Exception e){
//			e.printStackTrace();
//			throw e ;
//		}
//		finally
//		{
//			if(inStream!=null){
//			inStream.close();
//			}
//			if(outStream!=null){
//    	    outStream.close();
//			}
//		}
	    	    
	}
	
	//cut files paths
	public void cutFiles(Map<String,List<String>> filePaths){
	if(filePaths==null)	
	{
		logger.info("There is no new files to be archived. ");
	}
	else{
		for (String key: filePaths.keySet()) {
			CountryDao cd = new CountryDao();
			List<Location> cLocations = cd.getCountryLocations(key);
			String archPath=cLocations.get(0).getArchivingPath();
			List<String> countryPathList = filePaths.get(key);
				for(int p=0;p<countryPathList.size();p++){
					try{
					cut(countryPathList.get(p),archPath);
					}
					catch(Exception e){
						e.printStackTrace();
						logger.info("File  "+countryPathList.get(p)+" is not copied, and Error message "+e.getMessage()  );
					}
				}
			}
	}
	}
	
	//Archiving unused files
	public void archive(List<String> countries, List<String>  interfaces)
	{
		//Archiving
		logger.info("Begin Archiving " + countries + ", " +  interfaces );
		//Get local paths
		
		Map<String,String> localPaths=getLocalPaths(countries);
		Map<String,List<String>> filesPaths=getFilesPaths(localPaths);
		cutFiles(filesPaths);
		logger.info("End Archiving " + countries + ", " +  interfaces );
	  return ;	
	}
	
	public static void main(String[] args) {
		
		
		ChubJobs chubJobs = new ChubJobs();
		List interfaces = new ArrayList();
		interfaces.add("stock") ;
		chubJobs.FTPFiles("SYR", interfaces);
		
	}
	
	
	
	
	
	
	public List getFilesFromFTPLocation(Location loc, List<String> interfaces)
	{
		//1- this function work for location and its interfaces
		//2- prepare location parameters 
		//3- connect to client and check if it support compression mode or not ? and continue
		//4- prepare the remote path at FTP server and move to the remote path 
		// list all of remote files
		// list all local files
		// sort the remote list of files by modified date
		// get the supported interfaces for this location from the configration file.
		// filter the remote files list to be in the supported interfaces
		//loop over the remote file with min(remotefiles.size , requested minmum files)
		//for evry remoteFile check the following:
			//- if name not equal proceed to download 
		///	- if name is equal, check the file size, if its equal escape else proceed to download.
		//	- before download check if the file exist in the date boundry.
		
		//PropertyConfigurator.configure("C:\\Documents and Settings\\samy.a.omar\\workspace_3_7_2\\chub-executer\\src\\controller\\log4j.properties");
		int count=0;
		String ip = loc.getIp();
		String user = loc.getUsername();
		String pass = loc.getPass();
		String localPath=loc.getLocalPath();
		String remotePath=loc.getRemotePath();
		String days=loc.getDays();
		int numberOfDays=Integer.parseInt(days);
		String numFiles=loc.getNumOfFiles();
		int numberOfFiles=Integer.parseInt(numFiles);
		String fileExtension=loc.getFileExtension();
		FTPClient client=new FTPClient();
		FileOutputStream fos = null;
		FileOutputStream local = null;
		boolean flag=true;
		
		List totalDownloadedFilesNames =new ArrayList();
		List downloadedFilesList=new ArrayList();
		try{
			
           int maxnumberOFtry = 6 ;
			for(int s=0;s<maxnumberOFtry;s++)
			{ 
				         try {
								try {
									client.connect(ip);
								} catch (IllegalStateException el) {
									// Client already connected to ftp.unisyriagroup.com on port 21
									// catch this exception and continue
									el.printStackTrace();
								}
								client.login(user, pass);
								client.setType(FTPClient.TYPE_BINARY);
								break ;
							}
							catch (Exception e) {
								e.printStackTrace();
								if (s==maxnumberOFtry-1) 
								{
									throw e ;
								}
								else
								{
									//this.wait(1000L*20L);
									Thread.sleep(20000);
								}
								
							}	
			}
			
	
			
			
			if(client.isCompressionSupported())
			{
				LoggerController.logger.debug("compression is supported");
			}
			else
			{    	
				client.setCompressionEnabled(true);
				LoggerController.logger.debug("compression mode is enabled");	
			}
			
			
			if(remotePath.equals("."))
			{
				remotePath= client.currentDirectory();
			}
			client.changeDirectory(remotePath);
			//String s=client.currentDirectory();
			
		
			
			List<String> extensionList=getExtensionsList(loc);
			
			for(int i=0;i<extensionList.size();i++){
			String extension = extensionList.get(i);
			
			//////  1-
			FTPFile[] allRemoteFiles;
			List<FTPFile> remoteFilesList = new ArrayList();

     		allRemoteFiles=client.list("*" + extension);
			
			File localfile = new File(localPath);  
			File[] localfiles =localfile.listFiles();
			List<File> localfilesList = filestoList(localfiles);
	        //////  2-
			allRemoteFiles = sortFTPfilesByModifiedDate(allRemoteFiles);
			remoteFilesList = toList(allRemoteFiles);
			
			remoteFilesList = getSupportedInterfacesFiles(remoteFilesList, getLocationSupportedInterfacesNames(loc)) ;
			
			remoteFilesList = getSupportedInterfacesFiles(remoteFilesList, interfaces) ;
			
			// - 3
			remoteFilesList = getMaxNumberOfFilesInList(remoteFilesList, numberOfFiles);
			
			// - 4
			remoteFilesList =  getFilesModifiedDaysAgo(remoteFilesList, numberOfDays);
			
			// - 5
			
			
			// - 6
			

			//- 7
			remoteFilesList = getFilesWithDiffNameOrSize(remoteFilesList,localfilesList);
			
            //- 8
			List downloadedFilesNames = downloadFilesList(client,remoteFilesList,localPath);
			totalDownloadedFilesNames.addAll(downloadedFilesNames) ;
		}	
			return totalDownloadedFilesNames ;
		
		}
		catch(FTPDataTransferException f){
			//Executer.logger.debug(count);

		}

		catch(Exception e){
			e.printStackTrace();
		}
		finally {
			try {
				if (fos != null) {
					fos.close();
					try {
						client.disconnect(true);
					} catch (IllegalStateException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						} catch (FTPIllegalReplyException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						
					} catch (FTPException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
			catch (IOException ee) {
				ee.printStackTrace();
			}

		}
		return totalDownloadedFilesNames;
	}
	
	public Calendar validCalender(int numberOfDays)
	{
		Calendar c=Calendar.getInstance();
		c.add(Calendar.DATE, -1*numberOfDays);
		Date validDate=c.getTime();
		c.setTime(validDate);
		return c;
	}

	public Calendar fileCalender(Date fileDate)
	{
		Calendar cal=Calendar.getInstance();
		cal.setTime(fileDate); 
		return cal;
	}
	

	/*
	 * 
	 * 
	 * */
	public List<String> getLocationSupportedInterfacesNames(Location loc)
	{
		String matchingFiles=loc.getFileContains();
		String[] temp;
		List<String> fileNames=new ArrayList<String>();
		temp = matchingFiles.split("-");
		
	  	for(int i =0; i < temp.length ; i++)
	  	{
	  		fileNames.add(temp[i]);
	  		System.out.println(temp[i]);
	  	}
	  	return fileNames;
	}
	
	public List<String> getExtensionsList(Location loc)
	{
		String matchingFiles=loc.getFileExtension();
		String[] temp;
		List<String> extensions=new ArrayList<String>();
		temp = matchingFiles.split("-");
		
	  	for(int i =0; i < temp.length ; i++)
	  	{
	  		extensions.add(temp[i]);
	  		System.out.println(temp[i]);
	  	}
	  	return extensions;
	}
	
	/*public int iterator(List<String> remoteFiles,int numberOfFiles)
	{
		int iterator=0;
		int remoteFilesSize=remoteFiles.size();
		if(remoteFilesSize<numberOfFiles)
		{
			iterator=remoteFilesSize;
		}
		else if(remoteFilesSize>numberOfFiles)
		{
			iterator=numberOfFiles;
		}
		else if(remoteFilesSize==numberOfFiles)
		{
			iterator=numberOfFiles;
		}
		System.out.println("ITERATOR "+iterator);
		
		return iterator;
	}
	*/
	
	public FTPFile[] sortFTPfilesByModifiedDate(FTPFile[] allRemoteFiles)
	{
			FTPFile tempFile;
		for(int i=1;i<=allRemoteFiles.length;i++)
		{
			for(int j=0;j<(allRemoteFiles.length-1);j++)
			{
				if(allRemoteFiles[j+1].getModifiedDate().after(allRemoteFiles[j].getModifiedDate()))
				{
					tempFile=allRemoteFiles[j];
					allRemoteFiles[j]=allRemoteFiles[j+1];
					allRemoteFiles[j+1]=tempFile;
				}
			}
		}
		return allRemoteFiles ;
	}

	
	public List<FTPFile>  getSupportedInterfacesFiles(List<FTPFile> remoteFiles,List<String>  supportedInterfacesList)
	{
			List<FTPFile>  remoteFilesList = new ArrayList();
			for(int x=0;x<remoteFiles.size();x++)
			{
					for(int y=0;y<supportedInterfacesList.size();y++)
					{
						if(remoteFiles.get(x).getName().toLowerCase().contains(supportedInterfacesList.get(y).toLowerCase()))	
						{
							remoteFilesList.add(remoteFiles.get(x));
						}
					}
			}
			return remoteFilesList ;
	}
	
	
	public List<FTPFile> toList(FTPFile[] remoteFiles)
	{
		List<FTPFile>  remoteFilesList = new ArrayList();
		for(int x=0;x<remoteFiles.length;x++)
		{
			remoteFilesList.add(remoteFiles[x]);
		}	
       return remoteFilesList;
	}
	

	public List<File> filestoList(File[] remoteFiles)
	{
		List<File>  remoteFilesList = new ArrayList();
		for(int x=0;x<remoteFiles.length;x++)
		{
			remoteFilesList.add(remoteFiles[x]);
		}	
       return remoteFilesList;
	}
	
	
	public List<FTPFile> getMaxNumberOfFilesInList(List<FTPFile> remoteFiles, int maxNumberOfRequestedFiles)
	{
			List<FTPFile>  remoteFilesList = new ArrayList();
			for (int j=0;j<Math.min(remoteFiles.size(),maxNumberOfRequestedFiles);j++)
			{
				remoteFilesList.add(remoteFiles.get(j));
			}
			return remoteFilesList;
	}
	
	public List<FTPFile> getFilesModifiedDaysAgo(List<FTPFile> remoteFiles, int maxNumberOfRequestedDayes)
	{
			List<FTPFile>  remoteFilesList = new ArrayList();
			for (int j=0 ; j< remoteFiles.size() ;j++)
			{
				if(validCalender(maxNumberOfRequestedDayes).before(fileCalender( remoteFiles.get(j).getModifiedDate() )))
				{
					remoteFilesList.add(remoteFiles.get(j));
				}
			}
			return remoteFilesList;
	}
	
	public List<FTPFile> getFilesWithDiffNameOrSize(List<FTPFile> remoteFiles, List<File> localFilesList)
	{
			List<FTPFile>  remoteFilesList = new ArrayList();

			for (int j=0;j<remoteFiles.size();j++)
			{
				    boolean found = false ;
					for (int i=0;i<localFilesList.size();i++)
					{
						if( 
							( remoteFiles.get(j).getName().equalsIgnoreCase(localFilesList.get(i).getName()))
							&&	
							( remoteFiles.get(j).getSize() == localFilesList.get(i).length())
						   )
						{
							found = true ;
							break ;
						}
					}
					if(! found)
					{
						remoteFilesList.add(remoteFiles.get(j)) ;
					}	
			}
			return remoteFilesList;
	}
	
	
	
	
	public List<String> downloadFilesList(FTPClient client , List<FTPFile> remoteFiles, String localPath) throws Exception
	{
		List<String>  downloadedFilesListNames = new ArrayList();	
		
		for (int j=0 ; j< remoteFiles.size() ;j++)
		{
			File alreadyExistfile = new File(localPath +remoteFiles.get(j).getName());
			if(alreadyExistfile.exists())
			{
				alreadyExistfile.delete();
			}
			
			File localFile = new File(localPath +remoteFiles.get(j).getName());
			
			/*try {
				FileOutputStream  remote   = new FileOutputStream(remoteFiles.get(j).getName());
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}*/
			
			FileOutputStream local = null ;
			try {
				local = new FileOutputStream(localFile);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	

		         int maxnumberOFtry = 6 ;
					for(int s=0;s<maxnumberOFtry;s++)
					{ 
						         try {
										client.download(remoteFiles.get(j).getName(),local,0,null);
										downloadedFilesListNames.add(remoteFiles.get(j).getName());
										if(remoteFiles.get(j).getName().toLowerCase().endsWith(".zip"))
										{
											try
											{
												ZippingUtil zu = new ZippingUtil();
											    zu.unzip(localPath + remoteFiles.get(j).getName());
											}
											catch(Exception e)
											{
												e.printStackTrace();
												System.out.println("Failed to unzip the file " + remoteFiles.get(j).getName() + " the zipped file has been deleted.");
												// delete file
											}
										}
										break ;
									}
									catch (Exception e) 
									{
										e.printStackTrace();
										if (s==maxnumberOFtry-1) 
										{
											throw e ;
										}
										else
										{
											System.out.println("try number " + s);
											//this.wait(1000L*20L);
											 Thread.sleep(20000);
										}
										
									}	
					}
	}
		return downloadedFilesListNames ;
	}
	
	
	public boolean connectToClient(FTPClient client ,String ip ,String user ,String pass)
	{	
			try{
					int tries = 3 ;
				    client.connect(ip);
					client.login(user, pass);
					client.setType(FTPClient.TYPE_BINARY);
					
			}
			catch(Exception e)
			{
				e.printStackTrace();
				
				connectToClient(client , ip , user , pass);
			}
			return true ;
	}

	
	
	
	
	
	
}
